#include "qservermanager.h"

#include "qdeviceclient.h"
#include "qbasedefine.h"
#include "update/qclientupdate.h"
#include "database/qsendfile.h"

#include "../shared/qprojectcore.h"
#include "../shared/qdatamanager.h"
#include "../shared/qdata.h"
#include "../shared/xmlobject.h"
#include "../shared/driver/qabstractdriver.h"
#include "../shared/qdrivermanager.h"
#include "../shared/property/qabstractproperty.h"

QServerManager::QServerManager(tagDeviceInfo *info, QObject *parent):
    QObject(parent),
    m_device_info(info),
    m_device_client(new QDeviceClient(info)),
    m_client_update(new QClientUpdate),
    m_timer(new QTimer)
{
    connect(m_device_client,SIGNAL(server_online(tagServerInfo*)),this,SLOT(new_server(tagServerInfo*)));
    connect(m_device_client,SIGNAL(server_offline(QString)),this,SLOT(delete_server(QString)));

    m_device_client->search();

    connect(m_device_client,SIGNAL(recv_data(QByteArray)),this,SLOT(recv_data(QByteArray)));
    connect(m_timer,SIGNAL(timeout()),this,SLOT(timer()));
    m_timer->start(30);


    QDataManager* data=m_device_info->m_project_core->get_data_manager();

    connect(data,SIGNAL(data_value_changed(QString)),this,SLOT(data_changed(QString)));
    m_last_data_sync_time=QDateTime::currentDateTime();

    connect(m_device_info->m_project_core->get_driver_manager(),SIGNAL(report(QString)),
            this,SLOT(report_driver_message(QString)));
}

QServerManager::~QServerManager()
{
    qDeleteAll(m_servers);
    m_servers.clear();
    delete m_device_client;
    qDeleteAll(m_uuid_to_sendfile.values());
    m_uuid_to_sendfile.clear();
}

void QServerManager::new_server(tagServerInfo *device)
{
    if(m_uuid_to_server.value(device->m_uuid)!=NULL)
    {
        return;
    }
    tagServerInfo *info=new tagServerInfo;
    *info=*device;
    m_servers.append(info);
    m_uuid_to_server.insert(info->m_uuid,info);

    QSendFile *send=new QSendFile;
    m_uuid_to_sendfile.insert(device->m_uuid,send);
}

void QServerManager::delete_server(const QString &uuid)
{
    tagServerInfo *info=m_uuid_to_server.value(uuid);
    if(info!=NULL)
    {
        if(m_client_update->get_server_uuid()==uuid)
        {
            m_client_update->ready("");
        }
        m_sync_servers.removeAll(uuid);
        if(m_sync_servers.size()==0)
        {
            clear_locked();
        }

        if(m_uuid_to_sendfile.contains(uuid))
        {
            delete m_uuid_to_sendfile.value(uuid);
            m_uuid_to_sendfile.remove(uuid);
        }

        emit server_offline(uuid);
        m_servers.removeAll(info);
        m_uuid_to_server.remove(uuid);
        delete info;
    }
}

void QServerManager::recv_data(const QByteArray &data)
{
    int index=data.indexOf(":");
    if(index<0)
    {
        return;
    }

    QByteArray temp;

    temp=data.left(index);
    int code=temp.toInt();
    switch(code)
    {
    case UPDATE_CONTROL_CODE:
        recv_update(data.mid(index+1));
        break;
    case DATA_SYCN_CODE:
        recv_data_sync(data.mid(index+1));
        break;
    case DRIVER_LISTERN_CODE:
        driver_listern(data.mid(index+1));
        break;
    case DATABASE_LOAD_CODE:
        database_update(data.mid(index+1));
        break;
    default:
        break;
    }
}

void QServerManager::timer()
{
    QDateTime dt=QDateTime::currentDateTime();
    if(m_client_update->get_server_uuid()!="")
    {
        if(m_last_update_time.msecsTo(dt)>HAND_TIME_OUT)
        {
            m_client_update->ready("");
        }
    }
    if(m_data_changed_list.size()>0)
    {
        if(m_last_data_sync_time.msecsTo(dt)>30)
        {
            QDataManager* data=m_device_info->m_project_core->get_data_manager();
            XMLObject xml;
            xml.set_title("Data_Sync");
            xml.set_property("Device_uuid",m_device_info->m_uuid);
            xml.set_property("Operator","Changed");
            foreach(const QString &str,m_data_changed_list)
            {
                tagDataInfo *info=data->get_data(str);
                if(info!=NULL)
                {
                    XMLObject *o=new XMLObject(&xml);
                    o->set_title("Data");
                    o->set_property("uuid",str);
                    o->set_property("value",info->m_value.toString());
                }
            }

            if(xml.get_children().size()>0)
            {
                QString str=QString::number(DATA_SYCN_CODE)+":"+xml.write();
                m_device_client->send_data(str.toLocal8Bit(),-1);
            }
            m_data_changed_list.clear();
            m_last_data_sync_time=dt;
        }
    }
    else
    {
        m_last_data_sync_time=dt;
    }
}

void QServerManager::recv_update(const QByteArray &data)
{
    QByteArray array=data;
    int index=array.indexOf(":");
    if(index<0)
    {
        return;
    }
    QString server_uuid=array.left(index);
    array=array.mid(index+1);
    index=array.indexOf(":");
    if(index<0)
    {
        return;
    }
    QString client_uuid=array.left(index);
    QString d=array.mid(index+1);

    if(client_uuid!=m_device_info->m_uuid)
    {
        return;
    }
    tagServerInfo *server=m_uuid_to_server.value(server_uuid);
    if(server==NULL)
    {
        return;
    }

    QString str=QString::number(UPDATE_CONTROL_CODE)+":"+server_uuid+":"+client_uuid+":";
    if(d=="Start")
    {
        if(m_client_update->get_server_uuid()!="")
        {
            str+="Busy";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
        }
        else
        {
            m_last_update_time=QDateTime::currentDateTime();
            m_client_update->ready(server_uuid);
            str+="Start";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
        }
        return;
    }

    if(m_client_update->get_server_uuid()!=server_uuid)
    {
        return;
    }

    m_last_update_time=QDateTime::currentDateTime();
    if(d=="Done")
    {
        if(m_client_update->save())
        {
            str+="Done";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
            emit update_success();
        }
        else
        {
            str+="Done Failed";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
        }
        m_client_update->ready("");
    }
    else
    {
        array=array.mid(index+1);
        if(array.size()<4)
        {
            return;
        }
        uint sum=0;

        for(int i=0;i<array.size()-4;i++)
        {
            sum+=(array.at(i)&0xff);
        }
        QByteArray aa=array.right(4);
        uint c=((aa[0]&0xff)<<24)+((aa[1]&0xff)<<16)+((aa[2]&0xff)<<8)+(aa[3]&0xff);
        qsrand(QDateTime::currentDateTime().time().msec());
        if(sum==c)
        {
            m_client_update->recv_data(array.left(array.size()-4));
            str+="Ok";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
        }
        else
        {
            str+="Failed";
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
        }
    }
}

void QServerManager::recv_data_sync(const QByteArray &data)
{
    XMLObject xml;

    if(!xml.load(data,0))
    {
        return;
    }

    QString server_uuid=xml.get_property("Server_uuid");
    QString device_uuid=xml.get_property("Device_uuid");
    QString opr=xml.get_property("Operator");

    if(device_uuid!=m_device_info->m_uuid)
    {
        return;
    }

    tagServerInfo* server=m_uuid_to_server.value(server_uuid);

    if(server==NULL)
    {
        return;
    }

    if(opr=="Start")
    {
        QDataManager* data=m_device_info->m_project_core->get_data_manager();

        QList<QData*> list=data->get_all_datas();

        XMLObject o;
        o.set_title("Data_Sync");
        o.set_property("Device_uuid",m_device_info->m_uuid);
        o.set_property("Server_uuid",server_uuid);
        o.set_property("Operator","Start");
        m_sync_servers.removeAll(server_uuid);
        m_sync_servers.append(server_uuid);
        foreach(QData* d,list)
        {
            QList<tagDataInfo*> dl=d->get_datas();
            foreach(tagDataInfo *dd,dl)
            {
                XMLObject *x=new XMLObject(&o);
                x->set_title("Data");
                x->set_property("uuid",dd->m_uuid);
                x->set_property("group",d->get_name());
                x->set_property("name",dd->m_name);
                x->set_property("type",dd->m_type);
                x->set_property("information",dd->m_information);
                x->set_property("value",dd->m_value.toString());
                x->set_property("locked",dd->m_locked?"true":"false");
            }
        }

        QString str=QString::number(DATA_SYCN_CODE)+":"+o.write();
        m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
    }
    else if(opr=="End")
    {
        m_sync_servers.removeAll(server_uuid);
        if(m_sync_servers.size()==0)
        {
            clear_locked();
        }
    }
    else if(opr=="Locked")
    {
        QDataManager* data=m_device_info->m_project_core->get_data_manager();
        QString uuid=xml.get_property("uuid");
        QString value=xml.get_property("locked");
        tagDataInfo* info=data->get_data(uuid);
        if(info!=NULL)
        {
            bool b=value=="true";
            if(info->m_locked!=b)
            {
                info->m_locked=b;

                QString ss=QString::number(DATA_SYCN_CODE)+":";
                XMLObject o;
                o.set_title("Locked");
                o.set_property("Operator","Locked_Changed");
                o.set_property("uuid",uuid);
                o.set_property("Device_uuid",device_uuid);
                o.set_property("locked",value);
                o.set_property("value",info->m_value.toString());
                ss+=o.write();
                m_device_client->send_data(ss.toLocal8Bit(),-1);
            }
        }
    }
    else if(opr=="SetValue")
    {
        QDataManager* data=m_device_info->m_project_core->get_data_manager();
        QString uuid=xml.get_property("uuid");
        QVariant value=xml.get_property("value");
        tagDataInfo *info=data->get_data(uuid);
        if(info!=NULL)
        {
            if(info->m_locked)
            {
                info->m_value=value;
                info->m_data->setProperty(info->m_name.toLocal8Bit(),info->m_data->property(info->m_name.toLocal8Bit()));
            }
            else
            {
                info->m_data->setProperty(info->m_name.toLocal8Bit(),value);
            }
        }
    }
}

void QServerManager::driver_listern(const QByteArray &data)
{
    XMLObject xml;

    if(!xml.load(data,0))
    {
        return;
    }

    QString server_uuid=xml.get_property("Server_uuid");
    QString device_uuid=xml.get_property("Device_uuid");
    QString opr=xml.get_property("Operator");

    if(device_uuid!=m_device_info->m_uuid)
    {
        return;
    }

    tagServerInfo* server=m_uuid_to_server.value(server_uuid);

    if(server==NULL)
    {
        return;
    }

    if(opr=="Start")
    {
        QList<QAbstractDriver*> dirvers=m_device_info->m_project_core->get_driver_manager()->get_drivers();
        if(dirvers.size()==0)
        {
            return;
        }
        XMLObject xml_o;

        xml_o.set_title("Drivers");
        xml_o.set_property("Device_uuid",m_device_info->m_uuid);
        xml_o.set_property("Server_uuid",server_uuid);
        xml_o.set_property("Operator","Start");

        foreach(QAbstractDriver* dir,dirvers)
        {
            XMLObject *o=new XMLObject(&xml_o);
            o->set_title("Driver");
            o->set_property("Driver_Uuid",dir->get_uuid());
            QList<QAbstractProperty*> pros=dir->get_propertys();
            QAbstractProperty* pro;
            XMLObject *ooo=new XMLObject(o);
            ooo->set_title("Property");
            ooo->set_property("name","Name");
            ooo->set_property("value",dir->get_name());
            while(pros.size()>0)
            {
                pro=pros.takeFirst();
                if(pro->get_attribute("Report").toBool())
                {
                    XMLObject *oo=new XMLObject(o);
                    oo->set_title("Property");
                    oo->set_property("name",pro->get_attribute("show_name").toString());
                    oo->set_property("value",pro->get_value_text());
                }
                else
                {
                    pros+=pro->get_children();
                }
            }
        }

        QString str=QString::number(DRIVER_LISTERN_CODE)+":"+xml_o.write();
        m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
    }
}

void QServerManager::database_update(const QByteArray &data)
{
    XMLObject xml;

    if(!xml.load(data,0))
    {
        return;
    }

    QString server_uuid=xml.get_property("Server_uuid");
    QString device_uuid=xml.get_property("Device_uuid");
    QString opr=xml.get_property("Operator");

    if(device_uuid!=m_device_info->m_uuid)
    {
        return;
    }

    tagServerInfo* server=m_uuid_to_server.value(server_uuid);

    if(server==NULL)
    {
        return;
    }

    QSendFile *send=m_uuid_to_sendfile.value(server_uuid);

    if(send==NULL)
    {
        return;
    }

    if(opr=="Start")
    {

        QString name=xml.get_property("Name");

        if(!send->load(m_device_info->m_project_core->
                       get_project_host()->get_property_value("projectPath").toString()+"/"+name))
        {
            XMLObject x;
            x.set_title("Database");
            x.set_property("Operator","Error");
            x.set_property("Message","Load file failed!");
            x.set_property("Device_uuid",m_device_info->m_uuid);
            x.set_property("Server_uuid",server_uuid);
            QString str=QString::number(DATABASE_LOAD_CODE)+":"+x.write();
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
            return;
        }
        XMLObject x;
        x.set_title("Database");
        x.set_property("Operator","Start");
        x.set_property("Message",QString::number(send->count()));
        x.set_property("Device_uuid",m_device_info->m_uuid);
        x.set_property("Server_uuid",server_uuid);
        QString str=QString::number(DATABASE_LOAD_CODE)+":"+x.write();
        m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
    }
    else if(opr=="Next")
    {
        send->move_to_next();
        QByteArray data=send->get_send_data();
        if(data.size()==0)
        {
            XMLObject x;
            x.set_title("Database");
            x.set_property("Operator","End");
            x.set_property("Message","");
            x.set_property("Device_uuid",m_device_info->m_uuid);
            x.set_property("Server_uuid",server_uuid);
            QString str=QString::number(DATABASE_LOAD_CODE)+":"+x.write();
            m_device_client->send_data(str.toLocal8Bit(),server->m_server_ip);
            return;
        }
        else
        {
            m_device_client->send_data(QString::number(DATABASE_LOAD_CODE).toLocal8Bit()+":?"+data,server->m_server_ip);
        }
    }
    else if(opr=="Re-Send")
    {
        QByteArray data=send->get_send_data();
        m_device_client->send_data(QString::number(DATABASE_LOAD_CODE).toLocal8Bit()+":?"+data,server->m_server_ip);
    }
}

void QServerManager::data_changed(const QString &data_uuid)
{
    m_data_changed_list.removeAll(data_uuid);
    m_data_changed_list.append(data_uuid);
}

void QServerManager::clear_locked()
{
    QDataManager* data=m_device_info->m_project_core->get_data_manager();

    QList<QData*> datas=data->get_all_datas();

    foreach(QData* d,datas)
    {
        foreach(tagDataInfo *info,d->get_datas())
        {
            info->m_locked=false;
        }
    }
}

void QServerManager::report_driver_message(const QString &msg)
{
    XMLObject xml;
    xml.set_title("Driver_Listern");
    xml.set_property("Device_uuid",m_device_info->m_uuid);
    xml.set_property("Operator","Listern");
    xml.set_property("Message",msg);
    QString str=QString::number(DRIVER_LISTERN_CODE)+":"+xml.write();
    m_device_client->send_data(str.toLocal8Bit(),-1);
}
